#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <QDebug>
#include "Spi.h"

const QString Spi::SPI_DEVICE_FILE = "/dev/spidev0.%1";
const quint32 Spi::SPI_MODE = SPI_MODE_0;
const quint32 Spi::BITS_PER_WORD = 8;
const quint32 Spi::BITS_PER_SEC = 1000000;

Spi::Spi(quint32 spiDevice,
         QObject *parent) :
   QObject(parent),
   theSpiDevice(SPI_DEVICE_FILE.arg(spiDevice))
{
   // Blank intentionally
}

bool Spi::openPort()
{
   // Open SPI device file
   if(!theSpiDevice.open(QIODevice::ReadWrite))
   {
      qCritical() << "Failed to open SPI device file:" << theSpiDevice.errorString();
      return false;
   }

   // Set write mode
   if(ioctl(theSpiDevice.handle(), SPI_IOC_WR_MODE, &SPI_MODE) < 0)
   {
      qCritical() << "Failed to set SPI write mode:" << strerror(errno);
      return false;
   }

   // Set read mode
   if(ioctl(theSpiDevice.handle(), SPI_IOC_RD_MODE, &SPI_MODE) < 0)
   {
      qCritical() << "Failed to set SPI read mode:" << strerror(errno);
      return false;
   }

   // Set write word length
   if(ioctl(theSpiDevice.handle(), SPI_IOC_WR_BITS_PER_WORD, &BITS_PER_WORD) < 0)
   {
      qCritical() << "Failed to set SPI write word length:" << strerror(errno);
      return false;
   }

   // Set read word length
   if(ioctl(theSpiDevice.handle(), SPI_IOC_RD_BITS_PER_WORD, &BITS_PER_WORD) < 0)
   {
      qCritical() << "Failed to set SPI read word length:" << strerror(errno);
      return false;
   }

   // Set write data rate
   if(ioctl(theSpiDevice.handle(), SPI_IOC_WR_MAX_SPEED_HZ, &BITS_PER_SEC) < 0)
   {
      qCritical() << "Failed to set SPI write data rate:" << strerror(errno);
      return false;
   }

   // Set read data rate
   if(ioctl(theSpiDevice.handle(), SPI_IOC_RD_MAX_SPEED_HZ, &BITS_PER_SEC) < 0)
   {
      qCritical() << "Failed to set SPI read data rate:" << strerror(errno);
      return false;
   }

   return true;
}

void Spi::closePort()
{
   theSpiDevice.close();
}

bool Spi::transfer(QByteArray *data)
{
   struct spi_ioc_transfer transfer[data->length()];

   for(qint32 i = 0; i < data->length(); i++)
   {
      transfer[i].tx_buf = (quint64)data;
      transfer[i].len = data->length();
      transfer[i].bits_per_word = BITS_PER_WORD;
      transfer[i].speed_hz = BITS_PER_SEC;
      transfer[i].delay_usecs = 0;
      transfer[i].cs_change = 0;
   }

   if(ioctl(theSpiDevice.handle(), SPI_IOC_MESSAGE(data->length()), &transfer) < 0)
   {
      qWarning() << "Failed to transmit SPI data:" << strerror(errno);
      return false;
   }

   return true;
}
